// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "VmiTimer.h"

#include <cstdio>
#include <linux/prctl.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <unistd.h>

#include "Log/logging.h"

VmiTimerManage* VmiTimerManage::g_instance = nullptr;

VmiTimer::VmiTimer(unsigned int interval) : m_interval(interval), m_timerId(0)
{
    gettimeofday(&m_lastTime, nullptr);
}

void VmiTimer::Reset()
{
    (void)gettimeofday(&m_lastTime, nullptr);
}

void VmiTimer::SetTimerId(unsigned int timeId)
{
    m_timerId = timeId;
}

VmiTimerManage* VmiTimerManage::GetInstance()
{
    if (g_instance == nullptr) {
        g_instance = new (std::nothrow) VmiTimerManage();
        if (g_instance == nullptr) {
            ERR(" get timer manage instance fail ");
        }
    }
    return g_instance;
}

void VmiTimerManage::CloseInstance()
{
    if (g_instance != nullptr) {
        delete g_instance;
        g_instance = nullptr;
    }
}

VmiTimerThread::VmiTimerThread()
{
    mStatus = VMI_THREAD_STATUS_NEW;
}

VmiTimerThread::~VmiTimerThread()
{
    mStatus = VMI_THREAD_STATUS_EXIT;
    while (mStatus == VMI_THREAD_STATUS_EXIT) {
        (void)usleep(THREAD_STATUS_EXIT_SLEEP_INTERVAL_MS);
    }
}

void VmiTimerThread::Run()
{
    char threadname[] = "VmiTimerThread";
    if (prctl(PR_SET_NAME, threadname) != 0){
        ERR("prctl failed");
    }

    while (mStatus != VMI_THREAD_STATUS_EXIT) {
        VmiTimerManage::GetInstance()->Loop();
        (void)usleep(THREAD_STATUS_NONEXIT_SLEEP_INTERVAL_MS);
    }

    mStatus = VMI_THREAD_STATUS_NEW;
}

VmiTimerManage::VmiTimerManage()
{
    m_tcount = 1;
    if (pthread_mutex_init(&m_lock, nullptr) != 0){
        ERR("pthread_mutex_init failed");
    }
}

VmiTimerManage::~VmiTimerManage()
{
    for (auto& timer : m_timeVec) {//lint !e1072
        if (timer != nullptr) {
            delete timer;
            timer = nullptr;
        }
    }

    m_timeVec.clear();//lint !e681
    if (pthread_mutex_destroy(&m_lock) != 0){//lint !e529
        ERR("pthread_mutex_destroy failed");
    }

    DBG("VmiTimerManage deinit");
}

void VmiTimerManage::DeleteAll()
{
    if (pthread_mutex_lock(&m_lock) != 0){
        ERR("pthread_mutex_lock failed");
    }
    for (auto& timer : m_timeVec) {//lint !e1072
        if (timer != nullptr) {
            delete timer;
            timer = nullptr;
        }
    }

    m_timeVec.clear();//lint !e681

    if (pthread_mutex_unlock(&m_lock) != 0){
        ERR("pthread_mutex_unlock failed");
    }
}
void VmiTimerManage::AddTimer(VmiTimer* timer)
{
    DBG("begin add timer id=%d", timer->m_timerId);
    if (pthread_mutex_lock(&m_lock) != 0){
        ERR("pthread_mutex_lock failed");
    }
    DBG("inside add timer id=%d", timer->m_timerId);
    timer->SetTimerId(m_tcount);
    m_timeVec.push_back(timer);
    m_tcount++;
    timer->Reset();
    if (pthread_mutex_unlock(&m_lock) != 0){
        ERR("pthread_mutex_unlock failed");
    }
    DBG("end add timer id=%d", timer->m_timerId);
}

void VmiTimerManage::DeleteTimer(VmiTimer* timer)
{
    if (pthread_mutex_lock(&m_lock) != 0){
        ERR("pthread_mutex_lock failed");
    }
    std::vector<VmiTimer *>::iterator it;
    VmiTimer *t = timer;
    for (it = m_timeVec.begin(); it != m_timeVec.end();) {
        if ((t != nullptr) && (*it == t)) {
            delete t;
            t = nullptr;
            it = m_timeVec.erase(it);
            break;
        } else {
            ++it;
        }
    }

    if (pthread_mutex_unlock(&m_lock) != 0){
        ERR("pthread_mutex_unlock failed");
    }
}

void VmiTimerManage::Loop()
{
    (void)pthread_mutex_lock(&m_lock);
    if (m_timeVec.size() > 0) {
        struct timeval curTime = {};
        (void)gettimeofday(&curTime, nullptr);
        for (size_t i = 0; i < m_timeVec.size(); i++) {
            VmiTimer* timer = m_timeVec.at(i);
            if (timer == nullptr) {
                ERR("VmiTimerManage bad timer");
                (void)pthread_mutex_unlock(&m_lock);
                return;
            }

            long long diffSec = curTime.tv_sec - timer->m_lastTime.tv_sec;
            long long diffUsec = curTime.tv_usec - timer->m_lastTime.tv_usec;
            long long diff = diffSec * SECOND_UNIT_MS + diffUsec / SECOND_UNIT_MS;

            if (diff < 0) {
                ERR("VmiTimerManage bad timer diff=%lld", diff);
                continue;
            }
            if (diff > timer->m_interval) {
                timer->TimeOutCallback();
                timer->Reset();
            }
        }
    }
    (void)pthread_mutex_unlock(&m_lock);
}
